#include "connection.h"

Connection::Connection(bool onlyReceive)
{
    this->onlyReceive = onlyReceive;
    sequence = 0;
    remote_sequence = 0;
    connectionState = QAbstractSocket::UnconnectedState;
    timer = new QTimer();
	timer->setInterval(40);
}

Connection::Connection(QHostAddress a, quint16 p, bool onlyReceive)
{
    this->onlyReceive = onlyReceive;
    address = a;
    port = p;
    bindPort = port;
    sequence = 0;
    remote_sequence = 0;

    if (!onlyReceive)
    {
    	timer = new QTimer();
		timer->setInterval(40);
		startConnectionTimer(true);
        connect(timer, SIGNAL(timeout()),this,SLOT(packetCleanup()));
        lastPacketTime = time(0);

        tickNr = 0;
    }
    connectionState = QAbstractSocket::UnconnectedState;
}

void Connection::setAddress(QHostAddress addr)
{
    address = addr;
}

void Connection::setPort(quint16 p)
{
    port = p;
    bindPort = p;
}

void Connection::setOnlyReceive(bool onr)
{
    onlyReceive = onr;
}

void Connection::connToHost()
{
	sendQueue.clear();
	if (isLocalhostAddr() ) bindPort++;
    bind(bindPort,QUdpSocket::DontShareAddress);

    if (!onlyReceive)
    {

        connect(timer, SIGNAL(timeout()),this,SLOT(packetCleanup()));
        lastPacketTime = time(0);
        tickNr = 0;
    }

    if (isLocalhostAddr() )
    {
        while (state()==QAbstractSocket::UnconnectedState)
        {
            bindPort++;
            bind(bindPort);
        }
    }

    if (state()==QAbstractSocket::BoundState)
    {
        if (onlyReceive) emit connectionStateChanged(QAbstractSocket::ListeningState);
        connect(this,SIGNAL(readyRead()), this, SLOT(onNewDatagram()));
    }
}

void Connection::disconnFromHost()
{
    //abort();
	close();
	sendQueue.clear();
	receiveList.clear();
	reliableSendList.clear();
    startConnectionTimer(false);
    connectionState = QAbstractSocket::UnconnectedState;
    emit connectionStateChanged(connectionState);
}

void Connection::onNewDatagram()
{
    while (hasPendingDatagrams()) {
        QByteArray datagram;
        QHostAddress *host = new QHostAddress();
        quint16 *port = new quint16;
        datagram.resize(pendingDatagramSize());
        readDatagram(datagram.data(), datagram.size(), host, port);

        Packet *packet = new Packet(datagram);
        packet->setSenderAddress(*host);
        packet->setSenderPort(*port);
        delete port;
        delete host;
        emit newPacket(packet);
    }
}

void Connection::packetCleanup()
{
		//qDebug() << "cleanup";
	    //qDebug() << time(0) << lastPacketTime;
		/*qDebug() << "rec size" << receiveList.count();
		qDebug() << "rel size" << reliableSendList.count();
		qDebug() << "snd size" << sendQueue.count();*/

	    if ((time(0) - lastPacketTime) > 6)
	    {
	        disconnFromHost();
	        return;
	    }
	    if (!reliableSendList.isEmpty())
	    {
	        quint32 minSequence;
	        if (remote_sequence <= 32) minSequence = 0;
	        else minSequence = remote_sequence - 32;
	        quint32 now = time(0);
	        for (int i = reliableSendList.count() - 1; i >= 0; i--)
	        {
	            Packet* reliablePacket = reliableSendList.at(i);
	            if ((reliablePacket->getSequence() < minSequence) || (now - reliablePacket->getSendTime() >= 2))
	            {
	            	//qDebug() << "lost 1" << reliablePacket->getSequence() << minSequence;
	                sendQueue.push_front(reliablePacket);
	                reliableSendList.remove(i);
	            }
	        }
	    }

	    if (!sendQueue.isEmpty())
	    {
	        Packet* packet = sendQueue.at(0);
	        sendQueue.pop_front();
	        sendPacket(packet);
	    }

}

void Connection::sendPacket(Packet* packet) {
		packet->setSequence(sequence);
		packet->setSendTime(time(0));
		packet->setProtocolId(12345);
		sequence++;
		if (packet->getIsReliable())
		{
			reliableSendList.append(packet);
		}

		packet->setAck(remote_sequence);
		QBitArray arr(32,false);
		quint32 minSequence;
		if (packet->getAck() <= 32) minSequence = 0;
		else minSequence = packet->getAck() - 32;
		for (int i = receiveList.count() - 1; i >= 0; i--)
		{
			Packet* recPacket = receiveList.at(i);
			if (recPacket->getSequence() < minSequence)
			{
				receiveList.remove(i);
				delete recPacket;
			}
			else if (recPacket->getSequence() != packet->getAck())
			{
				arr.setBit(packet->getAck() - recPacket->getSequence() - 1);
			}
		}
		packet->setAckBitfield(arr);
		writeDatagram(packet->toByteArray(),address,port);
		if (!packet->getIsReliable())
		{
			delete packet;
		}
}

void Connection::queuePacket(Packet* packet)
{
    if (!onlyReceive)
    {
        sendQueue.push_back(packet);
    }
}

void Connection::sendPacket(const QByteArray data, bool reliable, bool queue)
{
    Packet* packet = new Packet();
    PacketData* d = new PacketData();
    d->appendString(data.data());
    packet->setData(d);
    packet->setIsReliable(reliable);

    if (queue) {
		queuePacket(packet);
	} else {
		sendPacket(packet);
	}
}

void Connection::sendPacket(PacketData* data, bool reliable, bool queue)
{
    Packet* packet = new Packet();
    packet->setData(data);
    packet->setIsReliable(reliable);
    if (queue) {
    	queuePacket(packet);
    } else {
    	sendPacket(packet);
    }
    delete data;
}

void Connection::processPacket(Packet* packet)
{
    lastPacketTime = time(0);
    if (!onlyReceive)
    {
        remote_sequence = qMax(remote_sequence, packet->getSequence());

        QBitArray arr = packet->getAckBitfield();
        quint32 maxSequence = packet->getAck();
        quint32 minSequence;
		if (remote_sequence <= 32) minSequence = 0;
		else minSequence = remote_sequence - 32;
		quint32 now = time(0);
        for (int i = reliableSendList.count() - 1; i >= 0; i--)
        {
            Packet* reliablePacket = reliableSendList.at(i);
            quint32 bitNr = maxSequence - reliablePacket->getSequence() - 1;
            //if (((bitNr > 32) || (!arr.at(bitNr))) && (reliablePacket->getSequence() != maxSequence))
            if ((reliablePacket->getSequence() < minSequence) || (now - reliablePacket->getSendTime() >= 2))
            {
            	//qDebug() << "lost 2";
                sendQueue.push_front(reliablePacket);

                reliableSendList.remove(i);
            }

            else
            {
            	//qDebug() << "accepted";
            	delete reliablePacket;
                reliableSendList.remove(i);
            }
        }


        receiveList << packet;
    }
    PacketData* packetData = new PacketData(packet->getData());
    runCommand(packetData, remote_sequence == packet->getSequence());
    delete packetData;
}

void Connection::startConnectionTimer(bool st)
{
	//qDebug() << "start stop" << st;
    if (!onlyReceive)
    {
        if (st && !timer->isActive())
        {
            timer->start();
        } else if (!st && timer->isActive())
        {
        	//qDebug() << "timer stopped";
            timer->stop();
        }
    }
}

void Connection::flushSendQueue()
{
    foreach (Packet* packet, sendQueue)
    {
        writeDatagram(packet->toByteArray(),address,port);
    }
    sendQueue.clear();
}

QAbstractSocket::SocketState Connection::getConnectionState()
{
    return connectionState;
}

void Connection::runCommand(PacketData* data, bool isNewestPacket)
{
}


QString Connection::getHostPortKey()
{
    return address.toString() + ":" + QString::number(port);
}

bool Connection::isLocalhostAddr()
{
    return (address.toString()=="localhost") || (address.toString() == "127.0.0.1");
}
